import queue
import threading
import sounddevice as sd
import numpy as np
import logging
import time

# 尝试导入opuslib，并记录它是否可用
has_opuslib = False
try:
    import opuslib
    has_opuslib = True
except ImportError:
    pass

from typing import Optional

logger = logging.getLogger("xiaozhi")

class AudioPlayer:
    def __init__(self, sample_rate: int = 16000, channels: int = 1, buffer_size: int = None, on_play_start=None, on_play_end=None, auto_start=True):
        # 将全局logger赋值给实例变量
        self.logger = logger
        
        # 计算更合适的缓冲区大小 (约500ms的音频，而不是100ms)
        if buffer_size is None:
            # 计算至少能容纳500ms音频的缓冲区大小，假设16位采样
            buffer_size = int(sample_rate * 0.5) * channels * 2  # 500ms音频
        
        # 音频参数
        self.sample_rate = sample_rate
        self.channels = channels
        self.buffer_size = buffer_size
        
        # 定义Opus解码可能的帧大小
        self.possible_frame_sizes = [960, 480, 240, 120, 1920, 2880]  # 常见的Opus帧大小（样本数）
        
        # 增加缓冲区大小，从500ms增加到1秒
        self.buffer_size = int(sample_rate * 1.0) * channels * 2
        
        # 回调函数
        self.on_play_start = on_play_start
        self.on_play_end = on_play_end
        
        # 创建互斥锁，用于保护音频缓冲区
        self.buffer_lock = threading.RLock()
        
        # 音频缓冲区（用于播放）
        self.audio_buffer = bytearray()
        
        # 音频队列（用于接收音频数据）
        self.audio_queue = queue.Queue()
        
        # 解码器（用于处理Opus数据）
        if has_opuslib:
            self.decoder = opuslib.Decoder(fs=sample_rate, channels=channels)
        
        # TTS停止标志
        self.tts_stop_received = False
        
        # 是否处于音乐播放模式
        self.is_music_mode = False
        self.frame_count = 0
        self.last_frame_time = time.time()
        self.frames_per_second = 0

        # 音乐播放优化参数
        self.optimal_buffer_size = self.buffer_size * 4  # 预缓冲4倍的缓冲区大小
        
        logger.info(f"初始化音频播放器: 采样率={self.sample_rate}Hz, 通道数={self.channels}, 缓冲区大小={buffer_size}")
        
        # 控制标志
        self.should_stop = False
        
        # 播放状态追踪
        self.is_playing = False
        self.last_audio_time = 0
        self.silence_duration = 0.5  # 0.5秒没有新数据认为播放完成
        
        # 结束播放定时器
        self._end_timer = None
        
        # 音频流
        self.stream: Optional[sd.OutputStream] = None
        
        # 增加预缓冲阈值，从500ms增加到1秒
        self.prebuffer_threshold = int(self.buffer_size * 2)  # 等待积累至少两个完整buffer的音频再开始播放
        self.buffer_low_threshold = int(self.buffer_size)  # 缓冲区过低阈值
        
        # 新增：跟踪标志，避免重复输出"首次添加音频数据"的日志
        self.audio_started = False
        
        # 新增：缓冲区统计
        self.buffer_underflows = 0
        self.buffer_reset_count = 0
        self.opus_decode_errors = 0
        self.opus_decode_successes = 0
        
        # 添加静音阈值，避免无限等待
        self.silence_threshold = 2.0  # 增加到2秒无音频判定为结束
        
        # 添加播放完成判断所需的额外参数
        self._silence_end_pending = False  # 标记是否正在等待静音结束判断
        self._silence_end_start_time = 0   # 静音开始的时间
        
        # 只有在auto_start为True时才自动启动
        if auto_start:
            self.start()

    def _audio_callback(self, outdata, frames, time_info, status):
        """音频回调函数，在音频设备需要数据时调用
        
        此函数必须非常高效，因为它在实时音频流的关键路径上
        处理时间过长会导致音频卡顿
        """
        try:
            start_time = time.time()  # 使用time模块记录开始时间，而不是time参数
            total_bytes_needed = frames * self.channels * 2  # 每个采样点2字节
            
            # 检查状态标志，如时钟偏移
            if status:
                # 只记录非underflow警告，因为underflow是预期的
                if status.output_underflow:
                    # 记录缓冲区不足的情况，但降低日志频率以减少overhead
                    if not hasattr(self, 'underflow_count'):
                        self.underflow_count = 0
                    self.underflow_count += 1
                    
                    # 每100次只记录一次日志，避免log flood
                    if self.underflow_count % 100 == 1:
                        self.buffer_underflows += 1
                        with self.buffer_lock:
                            buffer_size = len(self.audio_buffer)
                        self.logger.warning(f"音频输出缓冲区不足 (第{self.buffer_underflows}次): 缓冲区={buffer_size}, 需要={total_bytes_needed}")
                else:
                    # 其他严重错误，总是记录
                    self.logger.error(f"音频回调状态错误: {status}")
            
            # 快速检查播放状态，避免锁竞争
            if not self.is_playing:
                # 如果未播放状态，立即返回静音
                outdata.fill(0)
                return
                
            # 复制数据前获取一次锁，避免重复加锁解锁
            data = None
            with self.buffer_lock:
                buffer_size = len(self.audio_buffer)
                
                # 检查是否有足够的数据
                if buffer_size >= total_bytes_needed:
                    # 直接复制所需长度的数据，使用高效的切片和转换
                    data = bytes(self.audio_buffer[:total_bytes_needed])
                    # 移除已使用的数据
                    del self.audio_buffer[:total_bytes_needed]
                elif buffer_size > 0:
                    # 有部分数据，但不足以填满整个buffer
                    data = bytes(self.audio_buffer)
                    # 清空缓冲区
                    self.audio_buffer.clear()
            
            # 处理数据填充，在锁外执行以减少锁定时间
            if data is not None and len(data) > 0:
                # 重置静音持续时间计数
                self.silence_duration = 0
                
                if len(data) < total_bytes_needed:
                    # 数据不足，需要补充静音
                    missing_bytes = total_bytes_needed - len(data)
                    
                    # 为了避免音频卡顿，不记录每一次数据不足的情况
                    # 记录非常低的缓冲区状态
                    if buffer_size < self.buffer_low_threshold / 4:
                        # 每10次只记录一次，以减少日志开销
                        if not hasattr(self, 'low_buffer_count'):
                            self.low_buffer_count = 0
                        self.low_buffer_count += 1
                        
                        if self.low_buffer_count % 10 == 1:
                            self.logger.warning(f"音频缓冲区严重不足: 仅有 {buffer_size} 字节, 需要 {total_bytes_needed}")
                    
                    # 创建完整的输出数据：实际数据+静音填充
                    # 使用预分配的numpy数组以提高性能
                    out_buffer = np.zeros(total_bytes_needed, dtype=np.uint8)
                    out_buffer[:len(data)] = np.frombuffer(data, dtype=np.uint8)
                    
                    # 转换为float格式的音频数据
                    audio_data = out_buffer.view(np.int16).astype(np.float32) / 32767
                    outdata[:] = audio_data.reshape(-1, self.channels)
                    
                    # 淡出效果处理，减少爆音
                    if missing_bytes > total_bytes_needed // 2:
                        # 超过一半的数据是补充的，应用淡出效果
                        fade_samples = len(data) // (2 * self.channels)
                        if fade_samples > 10:  # 只在有足够样本时应用淡出
                            fade_factor = np.linspace(1.0, 0.0, fade_samples)
                            start_idx = len(outdata) - fade_samples
                            for i in range(fade_samples):
                                outdata[start_idx + i] *= fade_factor[i]
                else:
                    # 数据足够，直接填充
                    audio_data = np.frombuffer(data, dtype=np.int16).astype(np.float32) / 32767
                    outdata[:] = audio_data.reshape(-1, self.channels)
            else:
                # 无数据，填充静音
                outdata.fill(0)
                
                # 累计静音时间
                frame_duration = frames / self.sample_rate
                self.silence_duration += frame_duration
                
                # 更改检测播放结束的逻辑：
                # 1. 检查缓冲区是否为空
                # 2. 如果已收到TTS停止信号且缓冲区为空，判定为播放结束
                with self.buffer_lock:
                    buffer_empty = len(self.audio_buffer) == 0
                
                # 仅当缓冲区已空 AND 已收到TTS停止信号 才触发播放结束
                if buffer_empty and self.tts_stop_received and self.is_playing:
                    if not self._silence_end_pending:
                        self._silence_end_pending = True
                        self.logger.info(f"检测到缓冲区为空且已收到TTS停止信号，准备结束播放")
                        
                        # 使用Timer避免在音频回调中直接调用复杂函数
                        if not self._end_timer:
                            # 使用单独的线程触发播放结束检测
                            self._end_timer = threading.Timer(0.1, self._check_playback_end)
                            self._end_timer.daemon = True
                            self._end_timer.start()
                
                # 安全机制：如果持续静音超过较长时间(3秒)，即使缓冲区不为空，也考虑结束播放
                # 这是一个保护措施，避免因某些原因缓冲区一直不清空导致播放永远不结束
                elif self.silence_duration >= 3.0 and self.tts_stop_received and self.is_playing:
                    if not self._silence_end_pending:
                        self._silence_end_pending = True
                        self.logger.warning(f"检测到持续静音 ({self.silence_duration:.2f}秒) 且已收到TTS停止信号，强制结束播放")
                        
                        # 使用Timer避免在音频回调中直接调用复杂函数
                        if not self._end_timer:
                            # 使用单独的线程触发播放结束检测
                            self._end_timer = threading.Timer(0.1, self._check_playback_end)
                            self._end_timer.daemon = True
                            self._end_timer.start()
        
            # 计算处理时间，监控性能问题
            process_time = (time.time() - start_time) * 1000  # 转换为毫秒
            
            # 减少处理时间日志，以减轻系统负担
            if process_time > 20:  # 只记录超过20ms的处理时间
                # 非音乐模式下处理时间较长时发出警告
                if not self.is_music_mode and process_time > 30:
                    self.logger.warning(f"音频回调处理时间过长 {process_time:.2f}ms，可能导致音频卡顿")
                # 音乐模式下容忍更高的处理时间，因为通常有更大的缓冲区
                elif self.is_music_mode and process_time > 50:
                    # 每100帧只记录一次处理时间过长
                    if not hasattr(self, 'time_warning_count'):
                        self.time_warning_count = 0
                    self.time_warning_count += 1
                    
                    if self.time_warning_count % 100 == 1:
                        self.logger.warning(f"音乐模式: 音频回调处理时间过长 {process_time:.2f}ms")
        
        except Exception as e:
            # 确保任何异常都被捕获，防止音频流中断
            self.logger.error(f"音频回调处理出错: {e}")
            # 发生错误时，填充静音以保持音频流畅
            outdata.fill(0)
            
            # 记录详细错误信息以便调试
            import traceback
            self.logger.error(f"音频回调错误详情:\n{traceback.format_exc()}")

    def is_audio_playing(self) -> bool:
        """检查是否正在播放音频"""
        # 如果队列不为空或者正在播放，返回True
        with self.buffer_lock:
            buffer_not_empty = len(self.audio_buffer) > 0
        
        # 更全面的判断：
        # 1. 如果音频队列不为空
        # 2. 如果缓冲区还有数据
        # 3. 如果播放状态为True且没有长时间静音
        return (not self.audio_queue.empty() or 
                buffer_not_empty or 
                (self.is_playing and time.time() - self.last_audio_time <= self.silence_threshold))

    def process_audio(self, audio_data: bytes, is_opus: bool = True):
        """处理音频数据"""
        try:
            if is_opus:
                # 解码Opus数据
                pcm_data = self.decoder.decode(audio_data, self.buffer_size)
                audio_array = np.frombuffer(pcm_data, dtype=np.int16)
            else:
                # 直接使用PCM数据
                audio_array = np.frombuffer(audio_data, dtype=np.int16)

                # 如果不是Opus数据，需要确保数据大小合适
                if len(audio_array) > self.buffer_size:
                    # 分块处理
                    for i in range(0, len(audio_array), self.buffer_size):
                        chunk = audio_array[i:i + self.buffer_size]
                        # 转换为float32并放入队列
                        audio_float = chunk.astype(np.float32) / 32768.0
                        self.audio_queue.put(audio_float)
                    return

            # 转换为float32并放入队列
            audio_float = audio_array.astype(np.float32) / 32768.0
            self.audio_queue.put(audio_float)

        except Exception as e:
            logger.error(f"音频处理失败: {e}")

    def start(self):
        """启动音频输出流"""
        if self.stream is None or not self.stream.active:
            try:
                # 首先确保缓冲区清空
                with self.buffer_lock:
                    self.audio_buffer = bytearray()
                    self.audio_started = False
                
                # 增加缓冲区大小和块大小
                blocksize = 2048  # 使用更大的块以减少回调频率和处理负担
                self.logger.info(f"尝试启动音频流，使用块大小: {blocksize}")
                
                # 增加错误重试机制
                max_retries = 3
                for attempt in range(max_retries):
                    try:
                        self.stream = sd.OutputStream(
                            samplerate=self.sample_rate,
                            channels=self.channels,
                            callback=self._audio_callback,
                            blocksize=blocksize,
                            dtype=np.float32,  # 使用float32类型
                            latency='high'  # 使用高延迟设置以提高稳定性
                        )
                        self.stream.start()
                        logger.info(f"音频输出流已启动: 采样率={self.sample_rate}, 通道数={self.channels}, 块大小={blocksize}")
                        break  # 成功启动后退出循环
                    except Exception as e:
                        if attempt < max_retries - 1:
                            logger.warning(f"启动音频流失败 (尝试 {attempt+1}/{max_retries}): {e}，尝试不同参数...")
                            # 每次重试调整块大小
                            blocksize = max(1024, blocksize // 2)
                            continue
                        else:
                            raise  # 最后一次尝试仍失败，抛出异常
                
                # 重置统计计数器
                self.buffer_underflows = 0
                self.buffer_reset_count = 0
                self.opus_decode_errors = 0
                self.opus_decode_successes = 0
                
            except Exception as e:
                logger.error(f"启动音频流失败: {e}")
                import traceback
                logger.error(f"详细错误: {traceback.format_exc()}")
                
                # 尝试使用更保守的参数重新初始化
                try:
                    logger.info("尝试使用更保守的参数重新初始化音频流...")
                    # 完全移除blocksize参数
                    self.stream = sd.OutputStream(
                        samplerate=self.sample_rate,
                        channels=self.channels,
                        callback=self._audio_callback,
                        dtype=np.float32, 
                        latency='high'  # 使用更高延迟
                    )
                    self.stream.start()
                    logger.info("使用保守参数成功启动音频流")
                except Exception as backup_e:
                    logger.error(f"备用音频流也启动失败: {backup_e}")
                    # 设置标志表示无法启动音频流
                    self.stream = None
                    logger.critical("无法启动音频流，音频播放功能将不可用")

    def stop(self, force=True):
        """停止音频播放
        
        Args:
            force (bool): 是否强制停止播放（清空缓冲区）
        """
        if not self.is_playing:
            self.logger.debug("停止播放：播放器已经是停止状态")
            return
            
        self.logger.info(f"停止音频播放 (强制={force})")
        
        # 记录一下停止时的缓冲区状态，用于调试
        with self.buffer_lock:
            buffer_size = len(self.audio_buffer)
        self.logger.debug(f"停止时缓冲区状态: {buffer_size} 字节")
        
        # 如果是强制停止，清空缓冲区
        if force:
            with self.buffer_lock:
                self.audio_buffer = bytearray()
                # 清空音频队列
                while not self.audio_queue.empty():
                    try:
                        self.audio_queue.get_nowait()
                    except:
                        break
            
            # 重置播放结束触发器标志
            if hasattr(self, '_end_triggered'):
                self._end_triggered = False
                
            # 重置消息处理器触发标志
            if hasattr(self, '_end_triggered_by_handler'):
                self._end_triggered_by_handler = False
            
            # 将播放状态设置为停止
            self.is_playing = False
            
            # 播放结束时，如果有回调函数，调用它
            if self.on_play_end:
                # 确保所有的标志状态一致
                if hasattr(self, 'tts_stop_received') and not self.tts_stop_received:
                    self.logger.warning("强制停止播放，但TTS停止标志未设置，设置为True以保持一致性")
                    self.tts_stop_received = True
                    
                # 触发回调
                self.on_play_end()
        else:
            # 非强制停止方式：保留缓冲区内容，让音频自然播放完
            self.logger.info(f"非强制停止：保留 {buffer_size} 字节的缓冲区数据继续播放")
            
            # 未停止播放状态，但标记将要停止
            # 在缓冲区播放完毕后会自动停止
            # 这种情况不会清除缓冲区，只是通知要停止
            
            # 只有在缓冲区为空时才设置停止状态和触发回调
            if buffer_size == 0:
                # 缓冲区为空，可以立即停止
                self.is_playing = False
                
                # 重置标志
                if hasattr(self, '_end_triggered'):
                    self._end_triggered = False
                if hasattr(self, '_end_triggered_by_handler'):
                    self._end_triggered_by_handler = False
                
                # 触发回调
                if self.on_play_end:
                    # 确保TTS停止标志设置为True
                    if hasattr(self, 'tts_stop_received') and not self.tts_stop_received:
                        self.logger.warning("缓冲区为空时停止播放，但TTS停止标志未设置，设置为True以保持一致性")
                        self.tts_stop_received = True
                        
                    self.on_play_end()
            else:
                # 缓冲区不为空，保留缓冲区内容，让音频自然播放完
                if hasattr(self, 'tts_stop_received'):
                    # 确保TTS停止标志设置为True
                    if not self.tts_stop_received:
                        self.logger.info(f"设置TTS停止标志为True，将在缓冲区播放完毕后({buffer_size}字节)触发播放结束")
                        self.tts_stop_received = True

    def handle_tts_event(self, event_type, text=""):
        """处理TTS事件（开始/结束/句子开始/句子结束）
        
        Args:
            event_type: 事件类型 'start', 'stop', 'sentence_start', 'sentence_end'
            text: 相关文本内容
        """
        if event_type == 'start':
            # TTS开始事件
            self.logger.debug("收到TTS开始事件")
            
            # 无论之前是什么状态，都重置TTS停止标志
            previous_state = self.tts_stop_received
            self.tts_stop_received = False
            
            # 记录状态变化
            if previous_state:
                self.logger.info("新TTS开始，重置TTS停止标志")
            else:
                self.logger.debug("TTS开始，确认TTS停止标志为False")
                
            # 清空缓冲区，为新的TTS语音做准备
            with self.buffer_lock:
                buffer_had_data = len(self.audio_buffer) > 0
                if buffer_had_data:
                    self.logger.debug(f"TTS开始: 清空现有缓冲区 ({len(self.audio_buffer)} 字节)")
                self.audio_buffer = bytearray()
                self.audio_started = False
                
                # 清空音频队列
                while not self.audio_queue.empty():
                    try:
                        self.audio_queue.get_nowait()
                    except:
                        break
            
            # 重置所有的音频播放状态标志
            if hasattr(self, '_end_triggered'):
                self._end_triggered = False
                self.logger.debug("重置播放结束触发标志")
            
            # 重置连续空缓冲区计数
            if hasattr(self, 'empty_buffer_count'):
                self.empty_buffer_count = 0
            
            # 重置音频帧计数器
            self.frame_count = 0
            
            # 重置缓冲区信息记录时间，确保立即记录首帧
            if hasattr(self, 'last_buffer_log'):
                self.last_buffer_log = 0
                
            # 重置音乐模式
            if hasattr(self, 'is_music_mode') and self.is_music_mode:
                self.exit_music_mode()
                
            # 确保播放器准备好开始播放
            if not self.is_playing:
                self.logger.debug("TTS开始: 确保播放器状态为播放就绪")
            
        elif event_type == 'stop':
            # TTS结束事件
            prev_state = self.tts_stop_received
            self.tts_stop_received = True
            
            # 检查缓冲区和队列状态
            buffer_empty = False
            queue_empty = False
            
            with self.buffer_lock:
                buffer_size = len(self.audio_buffer)
                buffer_empty = buffer_size == 0
            queue_empty = self.audio_queue.empty() if hasattr(self, 'audio_queue') else True
            
            # 详细日志记录，用于调试
            self.logger.info(f"收到TTS停止事件: 缓冲区为空={buffer_empty}, 缓冲区大小={buffer_size}字节, 队列为空={queue_empty}, 之前的停止状态={prev_state}")
            
            # 如果缓冲区和队列都为空，且播放器正在播放，立即结束播放
            if buffer_empty and queue_empty and self.is_playing:
                self.logger.info("检测到TTS停止后音频系统已空，主动触发播放结束")
                # 确保音频播放结束回调被调用
                if self.on_play_end:
                    self.is_playing = False
                    self.on_play_end()
            else:
                # 否则，让音频继续播放，直到缓冲区为空
                # _audio_callback 会在检测到缓冲区为空时触发播放结束
                self.logger.info(f"收到TTS停止信号，但缓冲区仍有 {buffer_size} 字节数据，等待播放完成")
                # 这里不需要额外操作，因为_audio_callback会处理播放完成的逻辑

        elif event_type == 'sentence_start':
            # 句子开始事件
            self.logger.debug(f"收到句子开始事件: {text[:20]}...")
            
            # 注意：不要总是重置TTS停止标志，这可能会导致问题
            # 仅当未收到停止事件时，才确保停止标志为False
            if not self.tts_stop_received:
                self.logger.debug("确保TTS停止标志为False（句子开始）")
                self.tts_stop_received = False
            else:
                self.logger.warning("警告：收到句子开始事件，但TTS停止标志已设置，这可能表明状态不一致")
            
        elif event_type == 'sentence_end':
            # 句子结束事件
            self.logger.debug(f"收到句子结束事件: {text[:20]}...")
            
            # 注意：这里不设置tts_stop_received标志，因为它会导致缓冲区被清空

    def play(self, audio_data: bytes, is_opus: bool = True):
        """播放音频数据"""
        if not self.stream or not self.stream.active:
            self.start()
        self.process_audio(audio_data, is_opus)

    def close(self):
        """资源释放"""
        # 取消定时器
        if hasattr(self, '_end_timer') and self._end_timer:
            self._end_timer.cancel()
            self._end_timer = None
            
        self.stop()
        if self.stream is not None and self.stream.active:
            try:
                self.stream.stop()
                self.stream.close()
                self.stream = None
                logger.info("音频播放器已关闭")
            except Exception as e:
                logger.error(f"关闭音频播放器失败: {e}")

    def add_audio_data(self, data, is_opus=True):
        """向缓冲区添加音频数据"""
        if data is None or len(data) == 0:
            return
        
        try:
            # 解码原始数据（如果是Opus格式）
            if is_opus:
                try:
                    pcm_data = self._decode_opus_data(data)
                    if pcm_data is None or len(pcm_data) == 0:
                        return  # 解码失败，跳过
                except Exception as e:
                    self.logger.error(f"Opus解码失败: {e}")
                    return
            else:
                # 如果不是Opus数据，直接使用
                pcm_data = data
            
            # 更新音乐模式检测
            if len(pcm_data) > 0:
                self.frame_count += 1
                self.detect_music_mode()
            
            # 如果停止标志已设置，不再添加数据
            if self.should_stop:
                self.logger.debug("播放器已标记为停止，不再添加音频数据")
                return
                
            # 首次添加数据触发处理逻辑
            if len(pcm_data) > 0 and not self.audio_started:
                self.audio_started = True
                self.logger.info("首次添加音频数据到缓冲区: %d 字节", len(pcm_data))
                
                # 记录音频帧信息
                frame_ms = (len(pcm_data) / 2) / self.sample_rate * 1000  # 每样本2字节
                samples = len(pcm_data) // 2
                self.logger.info(f"音频帧信息: {len(pcm_data)} 字节, {samples} 样本, 约 {frame_ms:.2f}ms")
                
            # 检查是否需要开始播放
            if not self.is_playing and self.is_music_mode:
                # 音乐模式下，累积足够数据再开始播放，防止卡顿
                if len(self.audio_buffer) < self.optimal_buffer_size * 0.5:  # 只有在缓冲区不足一半时才记录
                    self.logger.debug(f"正在缓冲音乐数据: {len(self.audio_buffer)} 字节 (目标: {self.optimal_buffer_size} 字节)...")
            
            # 确保音频流已启动
            if self.stream is None or not self.stream.active:
                self.start()
                
            with self.buffer_lock:
                original_buffer_size = len(self.audio_buffer)
                self.audio_buffer.extend(pcm_data)
                new_buffer_size = len(self.audio_buffer)
                
                # 记录缓冲区状态（每秒记录一次）
                current_time = time.time()
                if not hasattr(self, 'last_buffer_log') or current_time - self.last_buffer_log > 1.0:
                    self.last_buffer_log = current_time
                    if self.is_music_mode:
                        self.logger.debug(f"音乐播放: 音频缓冲区: {original_buffer_size}→{new_buffer_size} 字节 (+{len(pcm_data)} 字节)")
                    else:
                        self.logger.debug(f"音频缓冲区: {original_buffer_size}→{new_buffer_size} 字节 (+{len(pcm_data)} 字节)")
                
                # 首次添加数据时的处理
                if not self.is_playing:
                    # 在音乐模式下，等待缓冲区积累足够数据再开始播放
                    if self.is_music_mode and not self.reset_buffer_for_music():
                        # 还在等待缓冲区填充，暂不开始播放
                        return
                    
                    # 设置播放状态
                    if not self.on_play_start is None:
                        self.logger.info("添加首个音频数据，触发播放开始回调")
                        self.is_playing = True
                        self.on_play_start()
                
        except Exception as e:
            self.logger.error(f"添加音频数据时出错: {e}")
            import traceback
            self.logger.error(f"详细错误: {traceback.format_exc()}")

    def _decode_opus_data(self, opus_data):
        """解码Opus音频数据，添加自适应帧大小处理和备用解码器选项"""
        if not hasattr(self, 'decoder') or self.decoder is None:
            self.logger.error("没有可用的Opus解码器，无法解码音频数据")
            return None
            
        try:
            # 使用主解码器和预定义的帧大小集合尝试解码
            for frame_size in self.possible_frame_sizes:
                try:
                    pcm_data = self.decoder.decode(opus_data, frame_size)
                    self.opus_decode_successes += 1
                    
                    # 定期记录解码成功率（每100帧或每1000帧）
                    total_attempts = self.opus_decode_successes + self.opus_decode_errors
                    if total_attempts % 100 == 0:
                        success_rate = (self.opus_decode_successes / total_attempts) * 100
                        # 减少日志数量，只在成功率不是100%时记录
                        if success_rate < 100 or total_attempts % 1000 == 0:
                            self.logger.info(f"Opus解码成功率: {success_rate:.1f}% ({self.opus_decode_successes}/{total_attempts})")
                    
                    # 解码成功，记录使用的帧大小
                    if self.opus_decode_successes <= 3 or self.opus_decode_successes % 1000 == 0:
                        self.logger.info(f"使用帧大小 {frame_size} 成功解码Opus数据 (帧 #{self.opus_decode_successes})")
                    
                    return pcm_data
                except Exception as e:
                    # 只记录前几次解码失败的详细错误
                    if self.opus_decode_errors < 5:
                        self.logger.debug(f"使用帧大小 {frame_size} 解码失败: {e}")
                    continue
            
            # 如果主解码器所有帧大小都失败，尝试备用解码器
            if hasattr(self, 'backup_decoders') and self.backup_decoders:
                for i, backup_decoder in enumerate(self.backup_decoders):
                    for frame_size in self.possible_frame_sizes:
                        try:
                            pcm_data = backup_decoder.decode(opus_data, frame_size)
                            self.opus_decode_successes += 1
                            self.logger.info(f"使用备用解码器 #{i+1} 和帧大小 {frame_size} 成功解码Opus数据")
                            return pcm_data
                        except Exception:
                            continue
            
            # 所有尝试都失败
            self.opus_decode_errors += 1
            if self.opus_decode_successes + self.opus_decode_errors > 0:
                error_rate = (self.opus_decode_errors / (self.opus_decode_errors + self.opus_decode_successes)) * 100
                # 对于前10次错误或错误率较高时记录警告
                if self.opus_decode_errors <= 10 or error_rate > 5:
                    self.logger.warning(f"无法解码Opus数据，错误率: {error_rate:.1f}% (错误: {self.opus_decode_errors}, 成功: {self.opus_decode_successes})")
            else:
                self.logger.warning(f"无法解码首帧Opus数据，请检查音频格式")
            
            # 如果错误率很高且已有一定样本量，记录更详细的警告
            if self.opus_decode_errors > 10 and error_rate > 10:
                self.logger.error(f"Opus解码错误率过高: {error_rate:.1f}%，总尝试: {self.opus_decode_errors + self.opus_decode_successes}")
                # 记录当前帧大小参数供诊断
                self.logger.error(f"当前帧大小参数: {self.possible_frame_sizes}")
            
            # 返回空数据，不会添加到缓冲区
            return None
            
        except Exception as e:
            self.logger.error(f"解码Opus数据失败: {e}")
            import traceback
            self.logger.error(f"详细错误: {traceback.format_exc()}")
            return None

    def start_playback(self):
        """开始播放音频"""
        # 取消现有的结束定时器（如果有）
        if hasattr(self, '_end_timer') and self._end_timer:
            self._end_timer.cancel()
            self._end_timer = None
            
        self.is_playing = True
        self.last_audio_time = time.time()
        
        if self.on_play_start:
            self.on_play_start()
        logger.debug("开始播放音频")

    def pause_playback(self):
        """暂停播放以重新缓冲"""
        # 不完全停止，只是暂停播放状态
        self.is_playing = False
        logger.debug("暂停播放以重新缓冲") 

    def detect_music_mode(self):
        """检测是否应该切换为音乐播放模式"""
        # 基于音频帧计数器和缓冲区使用情况判断
        if not self.is_music_mode and self.frame_count > 100:
            # 如果收到了大量的音频帧但TTS没有停止，可能是在播放音乐
            current_time = time.time()
            
            # 计算平均帧率
            if hasattr(self, 'first_frame_time'):
                elapsed = current_time - self.first_frame_time
                if elapsed > 0:
                    frame_rate = self.frame_count / elapsed
                    # 如果帧率很高并且已经运行一段时间，判定为音乐模式
                    if frame_rate > 10 and elapsed > 3.0:  # 每秒超过10帧，且运行超过3秒
                        self.is_music_mode = True
                        self.last_frame_time = current_time
                        self.logger.info(f"检测到音乐播放模式 (帧率: {frame_rate:.1f}帧/秒, 累计帧数: {self.frame_count})")
                        # 增加缓冲区大小，以适应音乐流
                        self.prebuffer_threshold = self.optimal_buffer_size
                        return True
            else:
                # 记录第一帧时间
                self.first_frame_time = current_time
        
        return self.is_music_mode
    
    def _check_playback_end(self):
        """检查播放是否已经结束，并触发回调"""
        # 检查是否已经被消息处理器触发了结束回调
        if hasattr(self, '_end_triggered_by_handler') and self._end_triggered_by_handler:
            self.logger.debug("播放结束回调已被消息处理器触发，跳过")
            return
        
        # 检查播放状态
        if not self.is_playing or not self.stream:
            return
        
        # 检查缓冲区状态
        buffer_empty = False
        with self.buffer_lock:
            buffer_empty = len(self.audio_buffer) == 0 or len(self.audio_buffer) < 1024
        
        # 检查是否收到TTS停止信号
        tts_stopped = getattr(self, 'tts_stop_received', False)
        
        # 只有在缓冲区为空且收到TTS停止信号时才认为播放结束
        if buffer_empty and tts_stopped:
            # 检测到播放结束
            self.logger.info("检测到缓冲区为空且已收到TTS停止信号，准备结束播放")
            
            # 设置已触发标记，防止消息处理器重复触发
            self._end_triggered = True
            
            # 如果有回调函数，调用它
            if self.on_play_end:
                self.on_play_end()

    def exit_music_mode(self):
        """退出音乐播放模式"""
        if self.is_music_mode:
            duration = time.time() - self.last_frame_time
            self.logger.info(f"退出音乐播放模式 (持续: {duration:.1f}秒, 播放帧数: {self.frame_count})")
            self.is_music_mode = False
            # 恢复普通缓冲区大小
            self.prebuffer_threshold = self.buffer_size
            # 重置计数器
            self.frame_count = 0
            if hasattr(self, 'first_frame_time'):
                delattr(self, 'first_frame_time')
                
    def reset_buffer_for_music(self):
        """音乐模式下的缓冲区重置策略 - 改进版"""
        with self.buffer_lock:
            # 计算已缓冲数据时长（毫秒）
            buffer_size_ms = (len(self.audio_buffer) / 2) / self.sample_rate * 1000  # 2字节/样本
            
            # 检查缓冲区大小相对于目标的比例
            buffer_ratio = len(self.audio_buffer) / self.optimal_buffer_size
            
            # 如果已缓冲超过1秒的音频或者达到了目标的80%，就可以开始播放了
            if buffer_size_ms >= 500 or buffer_ratio >= 0.8:
                current_buffer_size = len(self.audio_buffer)
                # 记录缓冲区统计信息
                self.logger.info(f"音乐播放缓冲完成: {current_buffer_size} 字节 (~{buffer_size_ms:.0f}ms), "
                          f"缓冲区比例: {buffer_ratio*100:.1f}%")
                return True  # 缓冲区已准备好，可以开始播放
            
            # 缓冲区尚未准备好，继续等待
            if self.frame_count % 10 == 0:  # 减少日志频率
                self.logger.debug(f"音乐播放预缓冲中: {len(self.audio_buffer)} 字节 (~{buffer_size_ms:.0f}ms), "
                            f"达到目标的 {buffer_ratio*100:.1f}%...")
            
            return False  # 继续等待缓冲 